import { EventHandler, IObserver } from './Interface';

/**
 * 观察者类
 */
export class Observer<T = any> implements IObserver<T> {
  constructor(
    public callback: EventHandler<T>,
    private context: any,
    private once: boolean = false
  ) {}

  notify(event: T): void {
    this.callback.call(this.context, event);
  }

  compar(context: any): boolean {
    return context === this.context;
  }

  isOnce(): boolean {
    return this.once;
  }
}

/**
 * 基础事件发射器类
 */
export abstract class BaseEventEmitter<EventTypes = string> {
  protected listeners = new Map<EventTypes, Set<Observer>>();

  protected addListener(eventName: EventTypes, observer: Observer): this {
    const observers = this.listeners.get(eventName) || new Set();
    if (!this.listeners.has(eventName)) {
      this.listeners.set(eventName, observers);
    }
    observers.add(observer);
    return this;
  }

  protected removeListener(eventName: EventTypes, observer: Observer): void {
    const observers = this.listeners.get(eventName);
    if (observers) {
      observers.delete(observer);
      if (observers.size === 0) {
        this.listeners.delete(eventName);
      }
    }
  }

  protected removeAllListeners(eventName?: EventTypes): void {
    if (eventName) {
      this.listeners.delete(eventName);
    } else {
      this.listeners.clear();
    }
  }

  protected notifyListeners<T>(eventName: EventTypes, event: T): void {
    const observers = this.listeners.get(eventName);
    if (!observers) {
      return;
    }

    const oncesToRemove: Observer[] = [];

    observers.forEach((observer) => {
      observer.notify(event);
      if (observer.isOnce()) {
        oncesToRemove.push(observer);
      }
    });

    oncesToRemove.forEach((observer) =>
      this.removeListener(eventName, observer)
    );
  }
}
